打鐵趁熱,整理了一部分最近開發上學到的事情,希望讀者不吝予以指教!
本文將介紹並深入比較兩種主要的 PostgreSQL 資料庫連線管理方式,在開發網站或應用程式時,如何有效管理資料庫連線是一個重要的課題,想像資料庫就像是應用程式的資料中心,我們需要建立連線才能存取這些資料,而連線的管理方式,就會直接影響到應用程式的效能、穩定性和資源使用效率。
主要由三個核心組件組成,讓我們逐一了解他們的功能和特點。
這是整個架構的基礎層,負責管理資料庫連線池的核心功能:
from psycopg2 import pool
class DatabaseConfig:
_connection_pool = None
@classmethod
def initialize(cls, minconn=1, maxconn=10):
"""初始化資料庫連線池"""
if cls._connection_pool is None:
cls._connection_pool = pool.ThreadedConnectionPool(
minconn,
maxconn,
host="localhost",
database="mydb",
user="user",
password="password"
)
@classmethod
def get_connection(cls):
"""從連線池獲取連線"""
return cls._connection_pool.getconn()
@classmethod
def return_connection(cls, conn):
"""將連線歸還給連線池"""
cls._connection_pool.putconn(conn)
@classmethod
def close_all(cls):
"""關閉所有連線"""
if cls._connection_pool:
cls._connection_pool.closeall()
這是中間層,主要負責連線的生命週期管理:
from contextlib import contextmanager
import logging
@contextmanager
def get_db_connection():
conn = None
try:
conn = DatabaseConfig.get_connection()
yield conn
conn.commit()
except Exception as e:
if conn:
conn.rollback()
logging.error(f"Database error: {str(e)}")
raise
finally:
if conn:
DatabaseConfig.return_connection(conn)
這是最上層,負責將連線池與應用程式整合:
from fastapi import FastAPI
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
# 應用啟動時初始化
logging.info("Initializing database connection pool")
DatabaseConfig.initialize(minconn=5, maxconn=20)
yield
# 應用關閉時清理
logging.info("Closing database connections")
DatabaseConfig.close_all()
app = FastAPI(lifespan=lifespan)
def get_user(user_id):
with get_db_connection() as conn:
with conn.cursor() as cur:
cur.execute("SELECT * FROM users WHERE id = %s", (user_id,))
return cur.fetchone()
資源管理
自動化處理
進階選項
簡單粗暴,適合剛學習資料庫應用的方法。
from fastapi import Depends
import asyncpg
async def get_database_connection():
conn = await asyncpg.connect(
host="localhost",
database="mydb",
user="user",
password="password"
)
return conn
@app.get("/users/{user_id}")
async def get_user(
user_id: int,
conn: asyncpg.Connection = Depends(get_database_connection)
):
row = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)
return row
為了讓非技術背景的讀者更容易理解這些概念,我用點餐流程來比喻請求與資料庫之間的關係,想像一下當你走進一間麥當勞:
有 1-5 個櫃檯:
特點:
沒有櫃檯,客人直接進廚房找餐點:
特點:
選擇合適的連線管理方式需要考慮多個因素:
對於大多數生產環境的應用來說,使用連線池管理是更好的選擇,它提供了更好的性能和資源管理能力,就像麥當勞不可能讓所有客人直接進廚房一樣,資料庫存取也需要有序且受控的管理機制。反之,對於開發環境或簡單應用,建立單一連線在需要快速迭代的開發測試需求上可能更有優勢。